home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The 640 MEG Shareware Studio 2
/
The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO
/
clang
/
nn.zip
/
MASTER.C
< prev
next >
Wrap
C/C++ Source or Header
|
1989-12-31
|
16KB
|
773 lines
/*
* nn master daemon
*
* maintains the article header database.
*/
#include <signal.h>
#include <errno.h>
#include "config.h"
#include "db.h"
/*
* nnmaster options:
*
* -e N expire a group if more than N articles are gone
* -r N repeat every N minutes
* -f foreground execution (use with -r)
* -y N retry N times on error
*
* -E expire by recolleting entire groups rather than copying files
* -C check consistency of database on start-up
* -b include 'bad' articles (disables -B)
* -B remove 'bad' articles (just unlink the files)
*
* -I initialize
* -t trace collection of each group
* -v print version and exit
* -u update even if active is not modified
* -w send wakeup to real master
* -Ltypes exclude 'types' entries from the log
* -D debug
*/
#include "options.h"
static int
initialize = 0,
prt_vers = 0,
unconditional = 0,
wakeup_master = 0,
expire_level = 1,
clean_to_expire = 0,
check_on_startup = 0,
repeat_delay = 0,
foreground = 0,
debug_mode = 0;
import int
ignore_bad_articles,
remove_bad_articles,
retry_on_error;
import char
*log_entry_filter;
export int
trace = 0,
#ifdef NNTP
silent = 1,
#endif
Debug = 0;
Option_Description(master_options) {
'I', Bool_Option( initialize ),
'v', Bool_Option( prt_vers ),
'u', Bool_Option( unconditional ),
'w', Bool_Option( wakeup_master ),
'e', Int_Option( expire_level ),
'r', Int_Option_Optional( repeat_delay, 10 ),
'f', Bool_Option( foreground ),
'y', Int_Option( retry_on_error ),
'E', Bool_Option( clean_to_expire ),
'C', Bool_Option( check_on_startup ),
'b', Bool_Option( ignore_bad_articles ),
'B', Bool_Option( remove_bad_articles ),
'L', String_Option( log_entry_filter ),
'D', Bool_Option( debug_mode ),
't', Bool_Option( trace ),
'\0',
};
import FILE *master_file;
import char news_active[];
static int rm_mpid_on_exit = 0;
extern long collect_group();
extern long expire_group();
main(argc, argv)
int argc;
char **argv;
{
time_t age_active;
register group_header *gh;
register int cur_group;
int col_article_count, col_group_count;
int exp_article_count, exp_group_count;
long temp;
time_t start_time;
FILE *m_pid;
umask(002); /* avoid paranoia */
init_global(1);
parse_options(argc, argv, (char *)NULL, master_options, (char *)NULL);
if (wakeup_master) {
if (!kill_master(SIGALRM) && errno == ESRCH)
printf("master is not running\n");
nn_exit(0);
}
if (prt_vers) {
print_version("Master: Release %R.%V.%P, Compilation %U\n");
nn_exit(0);
}
if (kill_master(SIGALRM)) {
printf("The master is already running\n");
nn_exit(0);
}
#ifdef NNTP
nntp_check();
#endif
if (initialize) {
build_master();
nn_exit(0);
}
if (!debug_mode) {
close(0);
close(1);
close(2);
if (open("/dev/null", 2) == 0) dup(0), dup(0);
}
if (repeat_delay && !debug_mode && !foreground) {
while ((temp = fork()) < 0) sleep(1);
if (temp) nn_exit(0);
process_id = getpid(); /* init_global saved parent's pid */
DETATCH_TERMINAL
}
rm_mpid_on_exit = 1;
m_pid = open_file(relative(lib_directory, "MPID"),
OPEN_CREATE|MUST_EXIST);
fprintf(m_pid, "%d\n", process_id);
fclose(m_pid);
log_entry('M', "Master started -r%d -e%d%s",
repeat_delay, expire_level, clean_to_expire ? " -E" : "");
if (check_on_startup) {
char cmd[FILENAME];
sprintf(cmd, "%s/nnadmin Z", BIN_DIRECTORY);
system(cmd);
log_entry('M', "Database validation completed");
}
repeat_delay *= 60;
init_digest_parsing();
open_master(OPEN_READ);
close_master();
open_master(OPEN_UPDATE);
again:
#ifdef NNTP
if (use_nntp) {
if (nntp_get_active() < 0) {
nntp_close_server();
current_group = NULL; /* for init_group */
log_entry('N', "Can't access active file --- %s",
repeat_delay ? "sleeping" : "terminating");
if (repeat_delay == 0)
nn_exit(1);
sleep(repeat_delay);
goto again;
}
}
#endif
age_active = file_exist(news_active, "fr");
#ifdef NNTP
if (!use_nntp)
#endif
if (age_active == (time_t)0)
sys_error("Cannot access active file");
temp = receive_admin();
if (!temp && !unconditional && age_active <= master.last_scan) {
if (repeat_delay == 0)
goto out;
if (s_hangup) goto out;
#ifdef NNTP
if (use_nntp)
nntp_cleanup();
#endif /* NNTP */
if (debug_mode)
printf("NONE (*** SLEEP ***)\n");
else {
if (trace) log_entry('T', "none");
sleep(repeat_delay);
}
if (s_hangup) goto out;
goto again;
}
unconditional = 0; /* only first pass */
time(&start_time);
col_article_count = col_group_count = 0;
exp_article_count = exp_group_count = 0;
visit_active_file();
for (cur_group = 0; cur_group < master.number_of_groups; cur_group++) {
if (s_hangup) break;
gh = &active_groups[cur_group];
if (gh->last_l_article > gh->last_article ||
gh->first_l_article > gh->first_article) {
log_entry('X', "group %s renumbered", gh->group_name);
clean_group(gh);
goto do_collect;
}
if (expire_level > 0 && gh->last_l_article > 0) {
if ((gh->first_l_article + expire_level) <= gh->first_article) {
if (trace) log_entry('T', "%s expire level", gh->group_name);
gh->group_flag |= G_EXPIRE;
goto do_collect;
}
if (gh->first_article > gh->last_l_article) {
if (trace) log_entry('T', "%s expire void", gh->group_name);
clean_group(gh);
goto do_collect;
}
}
if (gh->last_l_article == gh->last_article) {
if (gh->group_flag & G_BLOCKED) goto unblock_group;
continue;
}
do_collect:
if (!init_group(gh)) {
if ((gh->group_flag & G_NO_DIRECTORY) == 0) {
log_entry('R', "%s: no directory", gh->group_name);
gh->group_flag |= G_NO_DIRECTORY;
}
gh->last_l_article = gh->last_article;
gh->first_l_article = gh->last_article; /* OBS: not first */
gh->group_flag &= ~(G_EXPIRE | G_BLOCKED);
save_group(current_group);
continue;
}
if (gh->group_flag & G_NO_DIRECTORY) {
/* The directory has been created now */
gh->group_flag &= ~G_NO_DIRECTORY;
clean_group(gh);
}
if (gh->group_flag & G_EXPIRE) {
if (clean_to_expire) {
temp = gh->first_article - gh->first_l_article;
clean_group(gh);
} else {
if ((gh->group_flag & G_BLOCKED) == 0) {
gh->group_flag |= G_BLOCKED;
save_group(gh);
}
temp = expire_group(gh);
}
if (temp) {
exp_article_count += temp;
exp_group_count++;
}
}
temp = collect_group(gh);
#ifdef NNTP
if (temp < 0) {
/* connection broken */
gh->group_flag &= ~G_EXPIRE; /* remains blocked */
save_group(gh);
age_active = master.last_scan; /* did not complete */
current_group = NULL; /* for init_group */
break;
}
#endif
if (temp > 0) {
col_article_count += temp;
col_group_count++;
}
unblock_group:
if (temp || (gh->group_flag & G_BLOCKED)) {
gh->group_flag &= ~(G_EXPIRE | G_BLOCKED);
save_group(gh);
}
}
/* if master is interrupted, all new articles may not be collected */
if (!s_hangup)
master.last_scan = age_active;
save_master();
if (exp_article_count)
log_entry('X', "Expire: %3d art, %2d gr, %2ld s %s",
exp_article_count, exp_group_count,
time((time_t *)0) - start_time,
col_article_count ? "(incl. collect)" : "");
if (col_article_count)
log_entry('C', "Collect: %3d art, %2d gr, %2ld s %s",
col_article_count, col_group_count,
time((time_t *)0) - start_time,
exp_article_count ? "(incl. expire)" : "");
if (!s_hangup && repeat_delay) {
#ifdef NNTP
if (use_nntp)
nntp_cleanup();
#endif /* NNTP */
if (!debug_mode) sleep(repeat_delay);
if (!s_hangup) goto again;
}
out:
nn_exit(0);
/*NOTREACHED*/
}
/*
* nn_exit() --- called whenever a program exits.
*/
nn_exit(n)
{
#ifdef NNTP
if (use_nntp)
nntp_cleanup();
#endif /* NNTP */
close_master();
if (rm_mpid_on_exit)
unlink(relative(lib_directory, "MPID"));
if (n)
log_entry('E', "Abnormal termination, exit=%d", n);
else
if (rm_mpid_on_exit)
log_entry('M', "Master terminated%s", s_hangup ? " (hangup)" : "");
exit(n);
}
/*
* add new group to master file
*/
group_header *add_new_group(name)
char *name;
{
register group_header *gh;
FILE *group_file;
static has_warned = 0;
if (master.free_groups <= 0) {
if (!has_warned) {
log_entry('R', "NO MORE FREE GROUP SLOTS -- RESTART MASTER");
has_warned++;
}
return NULL;
}
master.free_groups--;
gh = &active_groups[master.number_of_groups];
sorted_groups[master.number_of_groups] = gh;
gh->group_name_length = strlen(name);
gh->group_name = (char *)malloc(gh->group_name_length + 1);
mem_check(gh->group_name, gh->group_name_length, "bytes for group name");
strcpy(gh->group_name, name);
gh->group_num = master.number_of_groups++;
group_file = open_groups(OPEN_UPDATE|MUST_EXIST);
fseek(group_file, master.next_group_write_offset, 0);
name[gh->group_name_length] = NL;
Fwrite(name, sizeof(char), gh->group_name_length + 1, group_file);
name[gh->group_name_length] = NUL;
fclose(group_file);
master.next_group_write_offset += gh->group_name_length + 1;
clean_group(gh);
save_master();
sort_groups();
log_entry('C', "new group: %s (%d)", gh->group_name, gh->group_num);
return gh;
}
save_group(gh)
group_header *gh;
{
int32 flag;
flag = gh->group_flag;
gh->group_flag &= G_MASTER_FLAGS;
if (!db_write_group(master_file, gh, gh->group_num))
write_error();
fflush(master_file);
gh->group_flag = flag;
}
save_master()
{
rewind(master_file);
if (!db_write_master(master_file, &master))
write_error();
fflush(master_file);
}
clean_group(gh)
register group_header *gh;
{
if (trace)
log_entry('T', "CLEAN %s", gh->group_name);
gh->first_l_article = 0;
gh->last_l_article = 0;
gh->index_write_offset = (off_t)0;
gh->data_write_offset = (off_t)0;
gh->group_flag &= ~G_EXPIRE;
gh->group_flag |= G_BLOCKED;
save_group(gh);
}
/*
* Build initial master file ; calls Initialize script and
* reads info from its standard output
*/
build_master()
{
char command[512];
char groupname[512];
group_header group;
FILE *group_file, *src;
int lcount, use_group_file;
printf("Confirm initialization by typing 'OK': ");
fl;
gets(command);
if (strcmp(command, "OK")) {
printf("No initialization\n");
nn_exit(0);
}
printf("Initializing master data base...");
fl;
if (chdir(lib_directory) < 0) /* so we can use open_file */
sys_error("lib");
#ifdef NNTP
if (use_nntp && nntp_get_active() < 0)
sys_error("Can't get active file");
#endif
/* check active file for duplicates */
sprintf(command, "awk 'NF>0{print $1}' %s | sort | uniq -d", news_active);
src = popen(command, "r");
for (lcount = 0; fgets(groupname, 512, src); lcount++) {
if (lcount == 0)
printf("\n%s contains duplicate entries for the following groups:",
news_active);
fputs(groupname, stdout);
}
pclose(src);
if (lcount > 0) {
printf("Do you want to repair this file before continuing ? (y)");
gets(command);
if (command[0] == NUL || command[0] == 'y' || command[0] == 'Y')
nn_exit(0);
}
/* if a "GROUPS" file exist offer to use that, else */
/* read group names from active file */
use_group_file = 0;
if (src = open_groups(OPEN_READ)) {
printf("\nA GROUPS file already exist -- reuse it? (y)");
fl;
gets(command);
if (command[0] == NUL || command[0] == 'y' || command[0] == 'Y') {
use_group_file = 1;
} else
fclose(src);
}
if (!use_group_file) {
strcpy(command, "awk 'NF>0{print $1}' ");
strcat(command, news_active);
strcat(command, " | sort -u");
src = popen(command, "r");
group_file = open_groups(OPEN_CREATE|MUST_EXIST);
}
open_master(OPEN_CREATE);
fseek(master_file, (off_t)sizeof(master), 0);
master.number_of_groups = 0;
while (fgets(groupname, 512, src)) {
group.group_num = master.number_of_groups++;
group.group_name_length = strlen(groupname) - 1; /* strip NL */
groupname[group.group_name_length] = NUL;
group.group_name = groupname;
group.group_flag = 0;
init_group(&group);
clean_group(&group);
/* moderation flag will be set by first visit_active_file call */
if (strcmp(groupname, "control") == 0)
group.group_flag |= G_CONTROL;
save_group(&group);
if (!use_group_file) {
groupname[group.group_name_length] = NL;
Fwrite(groupname, sizeof(char),
group.group_name_length + 1, group_file);
}
}
if (use_group_file) {
master.next_group_write_offset = ftell(src);
fclose(src);
} else {
master.next_group_write_offset = ftell(group_file);
fclose(group_file);
pclose(src);
}
master.last_scan = 0;
save_master();
close_master();
printf("done\n");
log_entry('M', "Master data base initialized");
fl;
}
/*
* receive commands from administrator
*/
receive_admin()
{
FILE *gate;
char buffer[128], *bp;
char command, *user_date;
long arg1, arg2;
int must_collect;
register group_header *gh;
gate = open_file(relative(lib_directory, "GATE"), OPEN_READ | OPEN_UNLINK);
if (gate == NULL) return 0;
sleep(2); /* give administrator time to flush buffers */
must_collect = 0;
while (fgets(buffer, 128, gate)) {
bp = buffer;
command = *bp;
if ((bp = strchr(bp, ';')) == NULL) continue;
arg1 = atol(++bp);
if ((bp = strchr(bp, ';')) == NULL) continue;
arg2 = atol(++bp);
if ((bp = strchr(bp, ';')) == NULL) continue;
user_date = ++bp;
if ((bp = strchr(bp, ';')) == NULL) continue;
*bp++ = NUL;
if (*bp != NL) continue;
log_entry('A', "RECV %c %ld %ld (%s)",
command, arg1, arg2, user_date);
if (arg1 >= 0 && arg1 < master.number_of_groups)
gh = &active_groups[arg1];
else
gh = NULL;
switch (command) {
case 'r':
repeat_delay = arg1;
continue;
case 'e':
expire_level = arg1;
continue;
case 'X': /* expire */
if (gh) {
gh->group_flag |= G_EXPIRE | G_BLOCKED;
save_group(gh);
break;
}
visit_active_file(); /* just in case */
Loop_Groups_Header(gh) {
if (gh->first_l_article + arg2 < gh->first_article) {
gh->group_flag |= G_EXPIRE | G_BLOCKED;
save_group(gh); /* could block here */
}
}
break;
case 'S': /* set flag */
gh->group_flag |= arg2;
save_group(gh);
continue;
case 'C': /* clear flag */
gh->group_flag &= ~arg2;
save_group(gh);
continue;
case 'R': /* recollect */
if (gh) {
clean_group(gh);
} else
Loop_Groups_Header(gh)
clean_group(gh);
break;
case 'U': /* unconditional pass */
unconditional++;
break;
case 'T': /* toggle trace flag */
trace = !trace;
continue;
default:
continue;
}
must_collect++;
}
fclose(gate);
return must_collect;
}
/*
* disk write with check -- halt if no space on disk
*/
Fwrite(buf, size, nitems, stream)
char *buf;
int size;
int nitems;
FILE *stream;
{
if (fwrite(buf, size, nitems, stream) != nitems)
write_error();
}
write_error()
{
/*
* should wait for problems to clear out rather than die...
*/
sys_error("DISK WRITE ERROR");
}
/*
* dummy routines - should never be called by master
*/
/*VARARGS*/
user_error()
{
dummy_error("user_error");
}
dummy_error(name)
char *name;
{
sys_error("Dummy routine called by master: %s", name);
}
#ifdef HAVE_JOBCONTROL
suspend_nn()
{}
#endif
#ifdef NNTP /* XXX */
msg() {}
#endif /* NNTP Bogus */